package distributed.lock;

import distributed.lock.redis.JedisClientRedisHandle;
import distributed.lock.redis.JedisRedisHandleAdapter;
import distributed.lock.redis.RedisHandle;
import distributed.lock.redis.RedisHandleAdapter;

import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.LockSupport;

public class RedisAqsLock implements RedisLock {
    RedisHandle redisHandle = new JedisClientRedisHandle();
    RedisHandleAdapter handleAdapter = new JedisRedisHandleAdapter();

    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            while (true) {
                try {
                    Thread t;
                    if (sync.getOwner() == null && sync.hasQueuedPredecessors()
                            && (t = sync.getFirstQueuedThread()) != null) {
                        System.out.println("通知 第一个线程 争夺锁");
                        LockSupport.unpark(t);
                    }
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    });


    public void startGatekeeper() {
        t.start();
    }


    public static ThreadLocal<String> randomValue = new ThreadLocal<String>() {
        @Override
        protected String initialValue() {
            return UUID.randomUUID().toString();
        }
    };

    private String key;
    private long expire = 1000;

    private final Sync sync = new RedisAqsLock.NonfairSync();

    @Override
    public void unlock() {
        sync.release(1);
    }

    @Override
    public void unlock(String key) {
        throw new IllegalMonitorStateException();
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        //加一个定时任务去唤醒 队列中 第一个线程（队列中第二个元素）
        return sync.tryAcquireNanos(1, unit.toNanos(time));
    }

    @Override
    public boolean tryLock(String key,long time, TimeUnit unit) throws InterruptedException {
        throw new IllegalMonitorStateException();
    }


    abstract class Sync extends AbstractQueuedSynchronizer {
        abstract void lock();

        //试着获取锁
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                System.out.println("争夺锁 :" + Thread.currentThread().getName());
                //竞争jvm状态
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    //加入竞争redis状态
                    if (handleAdapter.setnx(key, randomValue.get(), expire, TimeUnit.SECONDS, redisHandle)) {
                        return true;
                    } else {
                        //释放cas
                        setExclusiveOwnerThread(null);
                        setState(0);
                        return false;
                    }
                }
            } else if (current == getExclusiveOwnerThread()) {  //可重入
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
                try {
                    handleAdapter.comparAndDel(key, randomValue.get(), redisHandle);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("释放锁失败");
                }
            }
            setState(c);
            return free;
        }

        protected final boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }

        final boolean isLocked() {
            return getState() != 0;
        }


        private void readObject(java.io.ObjectInputStream s)
                throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

    final class NonfairSync extends Sync {
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }


    public RedisAqsLock(String key) {
        this.key = key;
    }

}
